package top.atcra.rpc.transport.request;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;
import top.atcra.rpc.compress.CompressorFactory;
import top.atcra.rpc.serialization.SerializerFactory;
import top.atcra.rpc.transport.message.RPCRequest;
import top.atcra.rpc.transport.message.RequestPayload;
import top.atcra.rpc.transport.utils.HeaderUtil;
import top.atcra.rpc.transport.utils.MessageFormatConstant;

@Slf4j
public class RPCRequestDecoder extends LengthFieldBasedFrameDecoder {

    public RPCRequestDecoder() {
        super(
                // 找到当前报文的总长度，截取报文，然后进行解析
                // 最大帧的长度
                MessageFormatConstant.MAX_FRAME_LENGTH,
                // 帧长度字段的偏移量,帧长度=报文头+报文体总长
                MessageFormatConstant.PROTOCOL_ID.length + MessageFormatConstant.VERSION_FILED_LENGTH + MessageFormatConstant.HEADER_FIELD_LENGTH,
                // 帧长度字段的长度
                MessageFormatConstant.FULL_FIELD_LENGTH,
                // 长度字段调整值（以下字段不存储于Request对象中，需要将它们的长度扣除）
                -(MessageFormatConstant.PROTOCOL_ID.length
                        + MessageFormatConstant.VERSION_FILED_LENGTH
                        + MessageFormatConstant.HEADER_FIELD_LENGTH
                        + MessageFormatConstant.FULL_FIELD_LENGTH),
                0
        );
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        var object = super.decode(ctx, in);
        if (object instanceof ByteBuf buf) {
            return rpcDecodeFrame(buf);
        }
        return null;
    }

    private Object rpcDecodeFrame(ByteBuf buf) {
//        log.debug("request:rpcDecodeFrame");
        HeaderUtil.checkProtocolVersion(buf);
        short header_len = buf.readShort();

        int full_len = buf.readInt();
        byte serializeType = buf.readByte();
        byte compressType = buf.readByte();
        long requestId = buf.readLong();
        long timestamp = buf.readLong();
        byte requestType = buf.readByte();
        RPCRequest request = RPCRequest.builder()
                .requestType(requestType)
                .serializeType(serializeType)
                .compressType(compressType)
                .requestId(requestId)
                .timestamp(timestamp).build();
        if (requestType != RPCRequest.RequestType.HEALTH_DETECT.getId()) {
            int body_len = full_len - header_len;
            byte[] body = new byte[body_len];
            buf.readBytes(body);

            var compressor = CompressorFactory.getCompressor(compressType);
            body = compressor.decompress(body);
            var serializer = SerializerFactory.getSerializer(serializeType);
            request.setBody(serializer.deserialize(body, RequestPayload.class));

            if (log.isDebugEnabled()) {
                log.debug("Server: Request with id {} Completed decoding.", request.getRequestId());
            }
        }

        buf.release();
        return request;
    }
}
